/*
 * p2spClient.cc
 *
 *  Created on: 2009-10-31
 *      Author: root
 */

#include "p2spStream.h"
#include <random.h>
#define P2SP_JITTER (Random::uniform())

static int header = 0;

void
StreamClientPlayTimer::expire(Event*)
{
	resched(t_->get_chunk_interval());
	t_->SlideOneChunk();
}

static class p2spStreamClientClass : public TclClass {
  public:
        p2spStreamClientClass() : TclClass("Application/p2sp/p2spStreamClient") {}
        TclObject* create(int, const char*const*) {
          return(new p2spStreamClient);
        }
} class_p2sp_client;

void
ReqTimer::expire(Event*)
{
  if(t_->p2sp_enable() == 1) {
        force_cancel();
        deadline = 0;
        t_->send_peer_req();
        reset(t_->peer_data_timeout_ * t_->chunksize_ * 10 / t_->wlan_rate_ + t_->peer_data_jitter_);

  }
}

void
ReqTimer::set(double t)
{
  sched(t);
  deadline = CURRENT_TIME + t;

}

void
ReqTimer::reset(double t)
{
  resched(t);
  deadline = CURRENT_TIME + t;

}

void
ReqTimer::freeze()
{
        force_cancel();
        if(deadline != 0){
			remain_time = deadline - CURRENT_TIME;
			assert(remain_time >= 0);
        }
}

void
ReqTimer::resume()
{
  if(remain_time != -1){
		reset(remain_time);
		remain_time = -1;
  }

}

void
SrvrReqTimer::expire(Event*)
{
  t_->send_srvr_req();
}

void
SrvrReqTimer::set(double t)
{
    sched(t);
}

void
SrvrReqTimer::reset(double t)
{
    resched(t);
}

void
ChunkMapReqTimer::expire(Event*)
{
  t_->req_chunkmap();
}

void
ChunkMapReqTimer::set(double t)
{
    sched(t);
}

void
ChunkMapReqTimer::reset(double t)
{
    resched(t);
}


void
DataTxTimer::expire(Event*)
{
  if(t_->p2sp_enable() == 1) {
      force_cancel();
      deadline = 0;
      t_->send_peer_data();
  }
  //resched(t_->data_tx_wnd());
}

void
DataTxTimer::set(double t)
{
    sched(t);
    deadline = CURRENT_TIME + t;
}

void
DataTxTimer::reset(double t)
{
    resched(t);
    deadline = CURRENT_TIME + t;
}

void
DataTxTimer::freeze()
{
        force_cancel();
        if(deadline != 0){
        remain_time = deadline - CURRENT_TIME;
        assert(remain_time >= 0);}
}

void
DataTxTimer::resume()
{
  if(remain_time != -1){
    reset(remain_time);
    remain_time = -1;
  }

}


//Constructor (also init instances of timers
p2spStreamClient::p2spStreamClient() : running_(0), req_timer_(this), data_tx_timer_(this), srvr_req_timer_(this),
							chunkmap_req_timer_(this), play_timer_(this), rx_done_(false), state_(IDLE), peer_down_(0),
							srvr_down_(0), peer_drop_(0), srvr_drop_(0), peer_up_(0), seqno_(0), timeslots_(0)
{

	bind("chunksize_", &chunksize_);
	bind("filesize_", &filesize_);
	bind("group_size_", &group_size_);
	bind("max_tx_wnd_", &max_tx_wnd_);
	bind("p2sp_enable_", &p2sp_enable_);
	bind("bufsize_", &bufsize_);
	bind_bw("wwan_rate_", &wwan_rate_);
	bind_bw("wlan_rate_", &wlan_rate_);
	bind("rd_wnd_", &rd_wnd_);

	bind("peer_req_jitter_",&peer_req_jitter_);
	bind("peer_data_jitter_",&peer_data_jitter_);
	bind("srvr_data_timeout_",&srvr_data_timeout_);
	bind("peer_data_timeout_",&peer_data_timeout_);
	bind("sel_scheme_", &sel_);
	bind_bw("playrate_", &playrate_);

}

int
p2spStreamClient::command(int argc, const char*const* argv) {
  Tcl& tcl = Tcl::instance();

  if(argc == 3) {
      if (strcmp(argv[1], "attach-agent") == 0) {
          agent_ = (Agent*) TclObject::lookup(argv[2]);
          if (agent_ == 0) {
            tcl.resultf("no such agent %s", argv[2]);
            return(TCL_ERROR);
           }
      }
  agent_->attachApp(this);
  return(TCL_OK);
  }

  return (Application::command(argc, argv));

}

void
p2spStreamClient::start() {

		assert(bufsize_ <= CHKBUFSIZE);

		chunk_interval_ = chunksize_*8/playrate_;
		switch(sel_) {
			  case 1:
				  sel_scheme_ = GREEDY;
				  break;
			  case 2:
				  sel_scheme_ = RAREST_FIRST;
				  break;
			  case 3:
				  sel_scheme_ = MOST_WANTED_GR;
				  break;
			  case 4:
				  sel_scheme_ = MOST_WANTED_RF;
				  break;
			  case 5:
				  sel_scheme_ = MOST_WANTED_RD;
				  break;
			  case 6:
				  sel_scheme_ = RD;
				  break;
			  case 7:
				  sel_scheme_ = MIXED;
				  break;
			  case 8:
				  sel_scheme_ = GREEDY_RD;
				  break;
			  case 9:
				  sel_scheme_ = RAREST_FIRST_RD;
				  break;
		}

		running_ = 1;
		start_time_ = CURRENT_TIME;
		buf_ready_ = 0;
		srvr_id_ = agent_->daddr();
		printf(""TIME_FORMAT"\t Node %d Start p2sp App Client\n", CURRENT_TIME, agent_->addr());
		req_chunkmap();


	  if( header == 0 ){
			header = 1;
			char logStart[256];
			sprintf(logStart,"NodeId,Timeslots,bufsize,continuity,startuptime\n");


			FILE *tfp, *tfp1;
			tfp = fopen("p2spstream.csv", "a");
			tfp1 = fopen("p2spdbg.txt","a");

			if (tfp != NULL){
			  fseek(tfp, 0, SEEK_END);
			  fputs(logStart, tfp);
			  fclose(tfp);
			}
			sprintf(logStart,"node,chunkid,from\n");
			if (tfp1 != NULL){
			  fseek(tfp1, 0, SEEK_END);
			  fputs(logStart, tfp1);
			  fclose(tfp1);
			}
	  }
}

void
p2spStreamClient::stop() {
  printf(""TIME_FORMAT"\t Node %d Stop p2sp App Client\n", CURRENT_TIME, agent_->addr());
  running_ = 0;

}

void
p2spStreamClient::initbuf(uint16_t chunk_head_id) {
	head = new chunkbuf_t[bufsize_];
	chunk_map_size_ = bufsize_/8 + (bufsize_%8 ? 1:0);
	if(head == NULL){
		printf("chunk buffer memory allocation failed!\n");
		return;
	}
	chunkbuf_t *p = head;
	p->chunkid = chunk_head_id ++;
	p->hasIt = 0;
	p->grouphasIt = 0;
	p->priority = 0;
	p->next = p+1;
	p->prev = head + bufsize_ - 1;
	p++;
	while(p != head + bufsize_ - 1) {
		p->chunkid = chunk_head_id ++;
		p->hasIt = 0;
		p->grouphasIt = 0;
		p->priority = 0;
		p->next = p+1;
		p->prev = p-1;
		p++;
	}
	p->chunkid = chunk_head_id ++;
	p->hasIt = 0;
	p->grouphasIt = 0;
	p->priority = 0;
	tail = p;
	tail->next = head;
	tail->prev = p-1;

	play_timer_.resched(get_chunk_interval());
}

void
p2spStreamClient::req_chunkmap() {

	  PacketData* p2sp_pkt_data = new PacketData(sizeof(p2sp_data_t));
	  p2sp_data_t* p2sp_data = (p2sp_data_t*)(p2sp_pkt_data->data());


	  if(running_){
	    p2sp_data->type_ = CHUNKMAP_REQ;

	    p2sp_data->nbytes_ = sizeof(p2sp_data_t);
	    p2sp_data->chunk_id_ = 0;
	    p2sp_data->src_id_ = agent_->addr();
	    p2sp_data->dst_id_ = srvr_id_;
	    p2sp_data->seqno_ =  ++seqno_;

//	    printf(""TIME_FORMAT"\t SEND: Client %d send chunk map req\n", CURRENT_TIME, agent_->addr());
	    agent_->sendto(p2sp_data->nbytes_, p2sp_pkt_data, 0, srvr_id_);
//	    printf(""TIME_FORMAT"\t SENT: Client %d send chunk map req\n", CURRENT_TIME, agent_->addr());
//	    printf("chunkmap request time out : %f \n", srvr_data_timeout_*(2*sizeof(p2sp_data_t) + chunk_map_size_)*10/wwan_rate_);

	    chunkmap_req_timer_.reset(srvr_data_timeout_*(2*sizeof(p2sp_data_t))*10/wwan_rate_);


	  }
}

void
p2spStreamClient::send_srvr_req() {
  //p2spAppData* p2sp_data = new p2spAppData();

	  PacketData* p2sp_pkt_data = new PacketData(sizeof(p2sp_data_t)+chunk_map_size_);
	  p2sp_data_t* p2sp_data = (p2sp_data_t*)(p2sp_pkt_data->data());

	  if(running_){
			p2sp_data->type_ = SRVR_REQUEST;

			p2sp_data->nbytes_ = sizeof(p2sp_data_t)+chunk_map_size_;
			p2sp_data->chunk_id_ = head->chunkid; //id of the first chunk in the buffer
			p2sp_data->src_id_ = agent_->addr();
			p2sp_data->dst_id_ = srvr_id_;
			p2sp_data->seqno_ =  ++seqno_;

			int delay = 2*(2*sizeof(p2sp_data_t) + chunk_map_size_ + chunksize_)*10/wwan_rate_/chunk_interval_+1;
			unsigned char *rq_bmp = new unsigned char[chunk_map_size_];
			int i,j,k;
			chunkbuf_t *p;
			for(i=0; i < chunk_map_size_; i++) {
				rq_bmp[i] = 0;
				for (j=0; j < 8; j++) {
					p = head;
					if (i*8 +j < delay) rq_bmp[i] = rq_bmp[i] | (1 << j);
					for(k = 0; k < i*8 +j; k++) p = p->next;
					rq_bmp[i] = rq_bmp[i] | (p->grouphasIt << j);
				}
				rq_bmp[i] = ~rq_bmp[i];
			}
			memcpy(p2sp_pkt_data->data() + sizeof(p2sp_data_t), rq_bmp, chunk_map_size_);
			delete rq_bmp;

			printf("node: %d, request chunk from %d, timeout: %f, delay: %d\n", agent_->addr(), head->chunkid, srvr_data_timeout_*(2*sizeof(p2sp_data_t) + chunk_map_size_ + chunksize_)*10/wwan_rate_, delay);
			agent_->sendto(p2sp_data->nbytes_, p2sp_pkt_data, 0, srvr_id_);

			srvr_req_timer_.reset(srvr_data_timeout_*(2*sizeof(p2sp_data_t) + chunk_map_size_ + chunksize_)*10/wwan_rate_);
	  }
}

void
p2spStreamClient::send_peer_req() {

	  if(p2sp_enable_ == 1) {
		  PacketData* p2sp_pkt_data = new PacketData(sizeof(p2sp_data_t) + chunk_map_size_);
		  p2sp_data_t* p2sp_data = (p2sp_data_t*)(p2sp_pkt_data->data());

		  if(running_){
				p2sp_data->type_ = PEER_REQUEST;

				p2sp_data->nbytes_ = sizeof(p2sp_data_t) + chunk_map_size_;
				p2sp_data->chunk_id_ = head->chunkid; //id of the first chunk in the buffer
				p2sp_data->src_id_ = agent_->addr();
				p2sp_data->dst_id_ = BROADCAST_ID;
				p2sp_data->seqno_ = ++seqno_;

				unsigned char *rq_bmp = new unsigned char[chunk_map_size_];
				int i,j,k;
				chunkbuf_t *p;
				for(i=0; i < chunk_map_size_; i++) {
					rq_bmp[i] = 0;
					for (j=0; j < 8; j++) {
						p = head;
						for(k = 0; k < i*8 +j; k++) p = p->next;
						rq_bmp[i] = rq_bmp[i] | (p->hasIt << j);
					}
					rq_bmp[i] = ~rq_bmp[i];
				}
				memcpy(p2sp_pkt_data->data() + sizeof(p2sp_data_t), rq_bmp, chunk_map_size_);
				delete rq_bmp;

				agent_->sendto(p2sp_data->nbytes_, p2sp_pkt_data, 0, BROADCAST_ID);
		  }
	  }
}

void
p2spStreamClient::send_peer_data(){
	  if(p2sp_enable_ == 1) {

			PacketData* p2sp_pkt_data = new PacketData(sizeof(p2sp_data_t) + chunksize_);
			p2sp_data_t* p2sp_data = (p2sp_data_t*)p2sp_pkt_data->data();
			int tx_chunk_id;
			tx_chunk_id = select_tx_chunk();
			if (running_ && tx_chunk_id != -1){
				  //data can be sent only if server is in running state
				  p2sp_data->type_ = PEER_DATA;
				  p2sp_data->chunk_id_ = tx_chunk_id; //change 1 to one choose chunk from request bitmap
				  p2sp_data->nbytes_ = sizeof(p2sp_data_t) + chunksize_; // size of p2sp application data packet, size is equal to chunk size
				  p2sp_data->src_id_ = agent_->addr();
				  p2sp_data->dst_id_ = BROADCAST_ID;
				  p2sp_data->seqno_ = ++seqno_;

				  agent_->sendto(p2sp_pkt_data->size(), p2sp_pkt_data, 0, BROADCAST_ID);
				  peer_up_ ++;
				  printf("SEND: Client %d send client %d peer up: %d\n", agent_->addr(), tx_chunk_id, peer_up_);
				  resetPendingBit(tx_chunk_id);
			}
	  }
}

void
p2spStreamClient::process_data(int size, AppData* data) {
  if(running_)
      if(data){

            //message process in p2sp Server
            PacketData* p2sp_pkt_data = (PacketData*) data;

            p2sp_data_t* p2sp_data = (p2sp_data_t*)(p2sp_pkt_data->data());

            if(p2sp_data->type_ == IS_RECVING){
                  /*if(p2sp_data->src_id_ != agent_->daddr() && state_ == IDLE)  {
                    state_ = BUSY;
                    req_timer_.freeze();
                    data_tx_timer_.freeze();
                    }*/

            }

            else if(p2sp_data->type_ == CHUNKMAP_CONF) {
//            	printf(""TIME_FORMAT"\t RECV: Client %d recv chunk map from chunk %d \n", CURRENT_TIME, agent_->addr(), p2sp_data->chunk_id_);
            	chunkmap_req_timer_.force_cancel();
            	initbuf(p2sp_data->chunk_id_);
            	buf_ready_ = 1;
            	send_srvr_req();
            	if(p2sp_enable_ == 1) {
            		req_timer_.reset(peer_req_jitter_*P2SP_JITTER);
            		data_tx_timer_.reset(.0);
            	}
            }

            else if(p2sp_data->type_ == PEER_REQUEST){
      /*        if(peer_tx_type_ == RANDOM){
      *           uint16_t tx_chunkid = select_chunk(hp_buf->req_bmp);
      *           if(tx_chunkid)
      *               send_p2sp_pkt(hp_buf->src_addr, tx_chunkid);
      *        }else if(peer_tx_type_ == PENDING)
      */
                  //later decide whether to cancel and restart req timer here
//                  printf(""TIME_FORMAT"\t P2SP: Client %d recv peer request from Node %d\n", CURRENT_TIME, agent_->addr(), p2sp_data->src_id_);
                  /*if(state_ == BUSY){
                        state_ = IDLE;
                        req_timer_.resume();
                        data_tx_timer_.resume();
                    }*/
				  if (buf_ready_ == 1) {
					  updatePendingBmp(p2sp_data->chunk_id_, (unsigned char*)(p2sp_pkt_data->data()+sizeof(p2sp_data_t)));
					  if(data_tx_timer_.status() != TIMER_PENDING)
							  data_tx_timer_.reset(peer_data_jitter_*P2SP_JITTER);
				  }

            }

            else if(p2sp_data->type_ == SRVR_REQUEST){
                    assert(false);
                    //DO NOTHING
                    //SHOULD NOT HAVE THIS CASE
            }

            else if(p2sp_data->type_ == SRVR_DATA){ //receive data, either from peer or server
            	if(buf_ready_ == 1) {
                    uint16_t rx_chunkid = p2sp_data->chunk_id_;
                    if (assemble(rx_chunkid))
							srvr_down_ ++;
                    else
                    	srvr_drop_ ++;

                    if(!rx_done_)
                        srvr_req_timer_.reset(0.0); //send_srvr_req();
                    else
                        srvr_req_timer_.force_cancel();
                    printf("RECV: Client %d recv chunk %d srvr down: %d, srvr drop: %d\n", agent_->addr(), rx_chunkid, srvr_down_, srvr_drop_);

                    char logdbg[256];
					sprintf(logdbg,"%d,%d,%d\n", agent_->addr(), rx_chunkid, p2sp_data->src_id_);

					FILE *tfp;
					tfp = fopen("p2spdbg.txt","a");

					if (tfp != NULL){
					  fseek(tfp, 0, SEEK_END);
					  fputs(logdbg, tfp);
					  fclose(tfp);
					}
            	}

            } else if(p2sp_data->type_ == PEER_DATA){
                /*if(state_ == BUSY){
                      state_ = IDLE;
                      req_timer_.resume();
                      data_tx_timer_.resume();
                  }*/
            	if(buf_ready_ == 1) {
						uint16_t rx_chunkid = p2sp_data->chunk_id_;
		//              printf(""TIME_FORMAT"\t P2SP: Client %d recv peer data %d from Node %d\n", CURRENT_TIME, agent_->addr(), rx_chunkid, p2sp_data->src_id_);
						if(assemble(rx_chunkid))
							peer_down_ ++;
						else
							peer_drop_ ++;
						if(!rx_done_ && req_timer_.status() != TIMER_PENDING) req_timer_.reset(peer_req_jitter_*P2SP_JITTER);
						else if(rx_done_) req_timer_.force_cancel();
						printf("RECV: Client %d recv chunk %d from client %d peer down: %d peer drop: %d\n", agent_->addr(), rx_chunkid, p2sp_data->src_id_, peer_down_, peer_drop_);
						resetPendingBit(rx_chunkid);
						char logdbg[256];
						sprintf(logdbg,"%d,%d,%d\n", agent_->addr(), rx_chunkid, p2sp_data->src_id_);

						FILE *tfp;
						tfp = fopen("p2spdbg.txt","a");

						if (tfp != NULL){
						  fseek(tfp, 0, SEEK_END);
						  fputs(logdbg, tfp);
						  fclose(tfp);
						}
            	}

        }

            delete data;

      }


}

bool
p2spStreamClient::assemble(uint16_t chunkid) {
  //assemble data
  //set req bitmap chunkid = 0 //no more required
	if(hasChunk(chunkid)){
		printf("Node:%d: %d---%d \t chunk %d not required \n", agent_->addr(), head->chunkid, tail->chunkid, chunkid);
		return false;
	}
	else {
		assert(chunkid >= head->chunkid && chunkid <= tail->chunkid);
		chunkbuf_t *p = head;
		/*while (p->chunkid < chunkid && p != tail) p = p->next;
		if(p->chunkid == chunkid) p->hasIt = 1;
		chunkid = chunkid - head->chunkid;*/
		int id = chunkid - head->chunkid;
		for(int i = 0; i < id; i++) p = p->next;
		assert(p->chunkid == chunkid);
		p->hasIt = 1;
		p->grouphasIt = 1;


		for(p=head; p != tail; p = p->next) {
			 if (p->hasIt == 0) {
				 return true;
			 }
		}
		if (p->hasIt == 0) return true;
		else rx_done();
	}
	return true;
}

bool
p2spStreamClient::hasChunk(uint16_t chunkid) {
	  uint16_t id;
	  if(chunkid < head->chunkid || chunkid > tail->chunkid){
		  printf("chunk is not required for not in range \n");
		  return true;
	  }
	  else
		  id = chunkid - head->chunkid;
	  assert(id >= 0 && id < bufsize_);
	  chunkbuf_t *p;
	  p = head;
	  for(int i = 0; i < id; i++) p = p->next;
	  assert(p->chunkid == chunkid);
	  return (p->hasIt == 1);
}

void
p2spStreamClient::dropchunk() {
  //add process later

}


void
p2spStreamClient::recv_msg(int nbytes, const char* msg) {

}

void
p2spStreamClient::SlideOneChunk() {
	chunkbuf_t *p;
	double start_legacy = 0;
	if(!running_ ) return;
	timeslots_ ++;
	p = head;
	//prob_[0] += p->hasIt;
	for(int i = 0; i < bufsize_; i++){
		prob_[i] = (prob_[i]*(timeslots_ - 1) + p->hasIt)/timeslots_ ;
		p = p->next;
	}

	for (int j = 0; j < bufsize_; j++) {
		start_legacy += prob_[j];
	}
	start_legacy = start_legacy * chunk_interval_;

	char logprob[256];
	sprintf(logprob,"%d,"TIME_FORMAT",%d,%f,%f\n", agent_->addr(), CURRENT_TIME, bufsize_, prob_[0], start_legacy);

	FILE *tfp;
	tfp = fopen("p2spstream.csv", "a");

	if (tfp != NULL){
	  fseek(tfp, 0, SEEK_END);
	  fputs(logprob, tfp);
	  fclose(tfp);
	}

	p = head;
	head = head->next;
	p->chunkid = tail->chunkid + 1;
	p->hasIt = 0;
	p->grouphasIt = 0;
	p->priority = 0;
	tail = p;

	if(rx_done_){
			rx_done_ = false;
			srvr_req_timer_.reset(0.0);
			req_timer_.reset(peer_req_jitter_*P2SP_JITTER);
	}



}


void
p2spStreamClient::rx_done() {
  //record time to finish stripping
  /*char logEnd[256];

  fetch_time_ = CURRENT_TIME - start_time_;

  sprintf(logEnd,"WWANrate:\t%f\tWLANrate:\t%f\tgroupsize:\t%d\tchunksize:\t%d\tNode\t%d\tstarttime:\t"TIME_FORMAT"\tfetchtime:\t"TIME_FORMAT"\twsize:\t%d\tlsize:\t%d\tUpsize:\t%d\treqjitter:\t%.5f\tdatajitter:\t%.5f\n",
      wwan_rate_, wlan_rate_, group_size_, chunksize_, agent_->addr(), start_time_, fetch_time_, srvr_down_, peer_down_,
      peer_up_, peer_data_jitter_, peer_req_jitter_);

  FILE *tfp;
  tfp = fopen("results.txt", "a");

  if (tfp != NULL){
    fseek(tfp, 0, SEEK_END);
    fputs(logEnd, tfp);
    fclose(tfp);
  }

  printf("%s", logEnd);*/
	rx_done_ = true;
	req_timer_.force_cancel();
	srvr_req_timer_.force_cancel();

}


void
p2spStreamClient::updatePendingBmp(uint16_t chunkid, unsigned char *rq_bmp) {

	uint16_t min_id, max_id, id;

	min_id = head->chunkid > chunkid? head->chunkid:chunkid;
	max_id = tail->chunkid > (chunkid+bufsize_-1)? (chunkid+bufsize_-1):tail->chunkid;
	for(id = min_id; id <= max_id; id++){
		assert(id >= head->chunkid && id <= tail->chunkid && id >= chunkid && id < chunkid+bufsize_);
		if ( (rq_bmp[(id-chunkid)/8] >> ((id-chunkid) % 8) & 1) == 1){
			chunkbuf_t *p = head;
			for(int k = 0; k < id - head->chunkid; k++) p = p->next;
			p->priority ++;
		} else {
			chunkbuf_t *p = head;
			for(int k = 0; k < id - head->chunkid; k++) p = p->next;
			p->grouphasIt = 1;
		}
	}
}

void
p2spStreamClient::resetPendingBit(uint16_t chunkid) {
  if(chunkid >= head->chunkid && chunkid <= tail->chunkid){
	  chunkbuf_t *p = head;
	  for(int k = 0; k < chunkid - head->chunkid; k++) p = p->next;
	  p->priority = 0;
  }
}

int
p2spStreamClient::select_tx_chunk() {
	//uint16_t tx_Pending_[MAXBMPS*32];
/*	int index = -1;
	int pending = 0;
	for(int i =0; i<chunk_map_size_*8; i++) {
	  if (pending < tx_Pending_[i] && hasChunk(i)) {
			  pending = tx_Pending_[i];
			  index = i;
	  }
	}
	//  printf("Node %d select chunk %d\n", agent_->addr(), index);
	return index;*/
	int id = -1;
	uint16_t i,rd_wnd;
	uint16_t max_pri_id[bufsize_];
	uint8_t max_pri = 0;
	chunkbuf_t *p;
	rd_wnd = Random::integer(rd_wnd_);

	if(sel_scheme_ == RAREST_FIRST) {
			for(p = tail->prev; p != head->next; p = p->prev) {
					if(p->priority > 0 && p->hasIt == 1) return p->chunkid;
			}
			if(p->priority > 0 && p->hasIt == 1) return p->chunkid;

	} else if(sel_scheme_ == GREEDY) {

			p = head->next;
			for(int i = 0; i < playrate_/wlan_rate_ ; i++) p=p->next;
			for(p; p != tail->prev; p = p->next){
					if(p->priority > 0 && p->hasIt == 1) return p->chunkid;
			}
			if(p->priority > 0 && p->hasIt == 1) return p->chunkid;

	} else if(sel_scheme_ == RAREST_FIRST_RD) {
			for(p = tail->prev; p != head->next; p = p->prev) {
					if(p->priority > 0 && p->hasIt == 1 && rd_wnd >= 0){
							if(rd_wnd == 0) return p->chunkid;
							id = p->chunkid;
							rd_wnd --;
					}
			}
			if(p->priority > 0 && p->hasIt == 1) return p->chunkid;
			else return id;


	} else if(sel_scheme_ == GREEDY_RD) {

			p = head->next;
			for(int i = 0; i < playrate_/wlan_rate_ ; i++) p=p->next;
			for(p; p != tail->prev; p = p->next){
					if(p->priority > 0 && p->hasIt == 1 && rd_wnd >= 0) {
							if (rd_wnd == 0) return p->chunkid;
							id = p->chunkid;
							rd_wnd --;
					}
			}
			if(p->priority > 0 && p->hasIt == 1) return p->chunkid;
			else return id;

	} else if(sel_scheme_ == MOST_WANTED_RF) {
			for(p = head->next->next; p != tail->prev; p = p->next) {
				if(p->priority >= max_pri  && p->hasIt == 1) {
					max_pri = p->priority;
					id = p->chunkid;
				}
			}
			if(p->priority >= max_pri && p->hasIt == 1) {
				max_pri = p->priority;
				id = p->chunkid;
			}
			if(max_pri > 0) return id;

	} else if(sel_scheme_ == MOST_WANTED_GR) {
			for(p = tail->prev->prev; p != head->next; p = p->prev) {
				if(p->priority >= max_pri && p->hasIt == 1) {
					max_pri = p->priority;
					id = p->chunkid;
				}
			}
			if(p->priority >= max_pri && p->hasIt == 1) {
				max_pri = p->priority;
				id = p->chunkid;
			}
			if(max_pri > 0) return id;

	} else if(sel_scheme_ == MOST_WANTED_RD) {
			for(p = head->next->next; p != tail->prev; p = p->next) {
				if(p->priority > max_pri && p->hasIt == 1){
					i = 0;
					max_pri = p->priority;
					max_pri_id[i] = p->chunkid;
				} else if(p->priority == max_pri && p->hasIt == 1){
					max_pri_id[++i] = p->chunkid;
				}
			}
			if(p->priority > max_pri && p->hasIt == 1){
				i = 0;
				max_pri = p->priority;
				max_pri_id[i] = p->chunkid;
			} else if(p->priority == max_pri && p->hasIt == 1){
				max_pri_id[++i] = p->chunkid;
			}

			if (max_pri > 0) {
				if(i > 0) return max_pri_id[Random::integer(i)];
				else return max_pri_id[0];
			}

	} else if(sel_scheme_ == MIXED) {
		for(p = tail; p->chunkid > (head->chunkid + tail->chunkid)/2; p = p->prev) {
				if(p->priority > 0 && p->hasIt == 1) return p->chunkid;
		}
		for(p = head; p->chunkid <= (head->chunkid + tail->chunkid)/2; p = p->next) {
				if(p->priority > 0 && p->hasIt == 1) return p->chunkid;
		}

	} else if(sel_scheme_ == RD) {
		for(p = head->next->next; p != tail->prev; p = p->next) {
			if(p->priority > 0 && p->hasIt == 1){
				max_pri_id[i++] = p->chunkid;
			}
		}
		if(p->priority > 0 && p->hasIt == 1){
			max_pri_id[i++] = p->chunkid;
		}
		if(i > 1) return max_pri_id[Random::integer(i-1)];
		else if(i == 1)  return max_pri_id[0];
	}


	return -1;
}

double
p2spStreamClient::data_tx_wnd() {
	  double tx_wnd;
	  if(peer_down_ == 0 && peer_up_ == 0) {
		  tx_wnd = max_tx_wnd_;
	  }else if(peer_down_ == 0) {
		  tx_wnd = group_size_*max_tx_wnd_;
	  }else {
		  tx_wnd = max_tx_wnd_*peer_up_*group_size_/peer_down_;
	  }
	  return Random::uniform(tx_wnd);
}

/*void
p2spStreamClient::UpdateReqChunkBmp() {
	chunkbuf_t *p = head;
	int i = 0;
	for (i = 0; i< chunk_map_size_-1; i++){
		req_bitmap[i] = (req_bitmap[i] >> 1) | ((req_bitmap[i+1] & 1) << 7);
	}
	req_bitmap[i] = (req_bitmap[i] >> 1) | ((~(tail->hasIt & 1)) << 7);
}*/
